// PGL stands for Programmable Graphics Library

/* TODO:
 * CLEANUP
 * 
 * Viewport transform
 */

#pragma once

#include <map>
#include <stack>
#include <vector>
#include <string>
using namespace std;

#include <CL/opencl.h>

#include "PGLTypes.h"
#include "PGLPipeline.h"

#define CHECKANDRET(e) if(e!=0) return e;

namespace PGL {
	// OpenCL implementation of the programmable graphics library
	class CLGL
	{
	public:
		CLGL();
		~CLGL();

	public:
		// Window/viewport
		void resize(cl_uint width, cl_uint height);
		void setViewport(cl_uint x, cl_uint y, cl_uint width, cl_uint height);

		// Clear buffers
		void setClearColor(cl_uchar4 clearColor);
		void clearDepthBuffer();

		// Vertex buffer handling
		pglMemObject createMemObject( void *input, size_t memSize );
		void unmapMemObject( pglMemObject pglm);
		void remapMemObject( pglMemObject pglm );

		// Run the generated pipeline
		void setDefaultPipelineArgs();
		void runPipeline();
		void enqueuePipeline();
		void finishPipeline();

		// Paint pixels to gl buffer
		void drawPixelsToGl();

		// Error enum lookup functions
		PGLErrorEnum getCurrentErrorAsEnum();
		string getCurrentErrorAsString();

	private:
		// Initialize OpenCL
		cl_int initOCL();
		cl_int initPlatform(string vendor);
		cl_int initDevice(cl_device_type devtype);
		cl_int initContext();
		cl_int initCommandQueues();

	private:
		// OpenCL pointers
		cl_platform_id mPlatformID;
		cl_device_id mCPUDeviceID;
		cl_device_id mGPUDeviceID;
		cl_command_queue mCPUCommandQueue;
		cl_command_queue mGPUCommandQueue;
		cl_context mContext;


	private:
		cl_int initDefaultKernels();
		cl_int initDefaultPixelStates();
		cl_int initDefaultVertexStates();
		cl_int initDefaultRasterStates();
		cl_int createProgramAndKernels(
			string fileName, vector<string> kernelNames, 
			cl_program *programOut, vector<cl_kernel> &kernelsOut);
		string readFileToString(string fileName);

	private:
		cl_program clpPixelProgram;
		cl_program clpVertexProgram;

	private:
		void unmapPixelBuffer(cl_command_queue cmdQueue,vector<cl_event> &waitEvents);
		void remapPixelBuffer(cl_command_queue cmdQueue,vector<cl_event> &waitEvents);

	private:
		// Initialize maps for lookup
		void initMapMatrixEnumToMatrixPointer();
		void initMapErrorEnumToErrorString();
	private:
		// Lookup maps
		map<PGLErrorEnum,string> mapErrorEnumToErrorString;

	private:
		// Scene information
		cl_uint gfxWidth;
		cl_uint gfxHeight;
		cl_uint4 gfxViewport;

		cl_uchar4 gfxClearColor;

		map<string,bool> gfxOptions;
		vector<pglLight> gfxLights;

	private:
		cl_mem clmPixelBuffer;
		cl_uchar4* pchPixelBuffer;

	private:
		pglPipeline pipeline;
		vector<cl_event> mPipelineEvents;

	public:
		// Pipeline states
		pglPipelineState clearColorState;
		pglPipelineState vertexTransformState;
		pglPipelineState rasterState;
		
	private:
		// Global error enum
		PGLErrorEnum mError;
		cl_int mOCLError;
	};
}
